<?php
include_once 'application/application.php';

class Page_mon {

    const NDF_NODATAFILE    = 'Нет файла данных';
    const NDF_DATAFILEEXP   = 'Файл данных не обновлялся';
    const NDF_DFNOACCESS    = 'Файл данных недоступен';
    const NDF_DFNOTABLE     = 'Отсутствует таблица в базе';

    const NL_NOLOGFILE      = 'Нет файла лога';
    const NL_LOGFILEEXP     = 'Нет связи продолжительное время';
    const NL_LOGNOACCESS    = 'Файл лога недоступен';




    public $data_path;
    public $log_path;
    public $log_load_path;
    public $err_log_path;

    public $action;
    public $error;



    public function __construct() {
        $this->action = Arr::getValue($_POST, 'action', false);

        DB::connect();
        if (!DB::state()) {
            $this->error = DB::getError();
        }


        // Log & files directories
        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
			$this->log_path = "E:/WWW/reports/log/";
			$this->err_log_path = "E:/WWW/reports/clients_program_update/clients_errors/err_";
			$this->log_load_path = "E:/WWW/reports/log/";
			$this->data_path = "E:/WWW/reports/data/";
		} else {
			$this->log_path = "/var/reports/log/";
			$this->err_log_path = "/var/reports/clients_program_update/clients_errors/err_";
			$this->log_load_path = "/var/reports/log/";
			$this->data_path = "/var/reports/data/";
		}
    }

    public function __destruct() {

    }





    private function getNoLink() {
        static $noLinkObj_;

        if (is_array($noLinkObj_)) return $noLinkObj_;

        $noLinkObj_ = array();
        $noLinkObj = array();
        $dbTables = array();
        $dbTables_all = getMSDbTables();
        $objects = getAllViews();
        $sut = 60*60*24; // секунд в сутках
        $yesterday = lastMomentOfDay(time()-$sut);

        if (!count($dbTables_all) || !count($objects)) {
            $this->error = 'Ошибка при работе с базой данных '.DB::getError().' ';
            return $noFilesObj;
        }

        // Выбираем только таблицы вида obj_№№№№ и рассортировываем объекты, которые к ним обращаются
        foreach ($dbTables_all as $table) {
            if (isValidDataTableName($table)) {
                $dbTables[$table] = array();//getObjectsByTable($table);
            }
        }

        // ищем файлы в соответствии с именами таблиц
        $fnames = array_keys($dbTables);
        foreach ($fnames as $name) {
            $filename = $this->log_path.'log_receive_'.$name;
            if (!is_file($filename)) {
                $filename = $this->log_load_path.'log_load_'.$name;
                if (!is_file($filename)) {
                    $noLinkObj[$name] = array(
                        'what'      => self::NL_NOLOGFILE,
                        'date'      => 1,
                        'datelm'    => 1,
                        'notice'    => '',
                        'objects'   => getViewsByTable($name)
                    );
                    continue;
                }
            }

            // свойства файла
            $stat = stat($filename);
            $lastModified = Arr::getValue($stat, 'mtime', FALSE);
            if (!$lastModified) {
                $noLinkObj[$name] = array(
                    'what'      => self::NL_LOGNOACCESS,
                    'date'      => 1,
                    'datelm'    => 1,
                    'notice'    => '',
                    'objects'   => getViewsByTable($name)
                );
                continue;
            }

            if ($lastModified<=$yesterday) { // файл сегодня не обновлялся
                //$lastModified = lastMomentOfDay($lastModified);
                $noLinkObj[$name] = array(
                    'what'      => self::NL_LOGFILEEXP,
                    'date'      => lastMomentOfDay($lastModified),
                    'datelm'    => $lastModified,
                    'notice'    => '',
                    'objects'   => getViewsByTable($name)
                );
            }

        }


        $noLinkObj_ = array();
        foreach ($noLinkObj as $key=>$val) {
            $objects = Arr::getValue($val, 'objects', array());
            $new = selectViewableObjectsFromSet($objects, array(0,1,2,3,4));
            if (!count($new) && count($objects)) continue;
            if (!count($new)) $val['notice'] = 'Нет отображений';
            $noLinkObj_[$key] = $val;
        }

        return $noLinkObj_;
    }




    private function getNoFiles() {
        $noFilesObj = array();
        $dbTables = array();
        $noLinkObj = array_keys($this->getNoLink());
        $dbTables_all = getMSDbTables();
        $objects = getAllViews();
        $sut = 60*60*24; // секунд в сутках
        $yesterday = lastMomentOfDay(time()-$sut);


        if (!count($dbTables_all) || !count($objects)) {
            $this->error = 'Ошибка при работе с базой данных '.DB::getError().' ';
            return $noFilesObj;
        }

        // Выбираем только таблицы вида obj_№№№№ и рассортировываем объекты, которые к ним обращаются
        foreach ($dbTables_all as $table) {
            if (isValidDataTableName($table)) {
                $dbTables[$table] = array();//getObjectsByTable($table);
            }
        }



        // ищем файлы в соответствии с именами таблиц
        $fnames = array_keys($dbTables);
        foreach ($fnames as $name) {

            if (in_array($name, $noLinkObj)) continue;

            $filename = $this->data_path.$name.'.xml';
            if (!is_file($filename)) {
                $noFilesObj[$name] = array(
                    'what'      => self::NDF_NODATAFILE,
                    'date'      => 1,
                    'datelm'    => 1,
                    'notice'    => '',
                    'objects'   => getViewsByTable($name)
                );
                continue;
            }

            // свойства файла
            $stat = stat($filename);
            $lastModified = Arr::getValue($stat, 'mtime', FALSE);

            if (!$lastModified) {
                $noFilesObj[$name] = array(
                    'what'      => self::NDF_DFNOACCESS,
                    'date'      => 1,
                    'datelm'    => 1,
                    'notice'    => '',
                    'objects'   => getViewsByTable($name)
                );
                continue;
            }

            if ($lastModified<=$yesterday) { // файл сегодня не обновлялся
                //$lastModified = lastMomentOfDay($lastModified);
                $noFilesObj[$name] = array(
                    'what'      => self::NDF_DATAFILEEXP,
                    'date'      => lastMomentOfDay($lastModified),
                    'datelm'    => $lastModified,
                    'notice'    => '',
                    'objects'   => getViewsByTable($name)
                );
            }

        }


        // Ищем файлы, для которых нет таблиц в базе
        $all_files = glob($this->data_path);
        if (is_array($all_files)) {
            foreach ($all_files as $filename) {
                if (!strpos($filename, '.xml')) continue;
                $fnarr = explode('.', $filename);
                if (is_array($fnarr) && isValidDataTableName($fnarr[0])) {
                    if (!is_array(Arr::getValue($dbTables, $fnarr[0], false))) {
                        $stat = stat($this->data_path.$filename);
                        $lastModified = Arr::getValue($stat, 'mtime', FALSE);

                        $noFilesObj[$fnarr[0]] = array(
                            'what'      => self::NDF_DFNOTABLE,
                            'date'      => lastMomentOfDay($lastModified),
                            'datelm'    => $lastModified,
                            'notice'    => '',
                            'objects'   => getViewsByTable($fnarr[0])
                        );
                    }
                }
            }
        }

        //return $noFilesObj;

        $noFilesObj_ = array();
        foreach ($noFilesObj as $key=>$val) {
            $objects = Arr::getValue($val, 'objects', array());
            $new = selectViewableObjectsFromSet($objects, array(0,1,2,3,4));
            if (!count($new) && count($objects)) continue;
            if (!count($new)) $val['notice'] = 'Нет отображений';
            $noFilesObj_[$key] = $val;
        }
        return $noFilesObj_;

        /*
        $noFilesObj_ = array();
        foreach ($noFilesObj as $key=>$val) {
            if (count($val['objects'])) $noFilesObj_[$key] = $val;
        }
        return $noFilesObj_;

         */
    }


    public function noLinkPage() {
        $params = array();
        $params['error'] = $this->error;
        $params['found'] = $this->getNoLink();

        $this->printOut(includeFile(APP_PAGE_PARTS.'monitoring_nolink.php', $params));
    }

    public function noFilesPage() {
        $params = array();
        $params['error'] = $this->error;
        $params['found'] = $this->getNoFiles();

        $this->printOut(includeFile(APP_PAGE_PARTS.'monitoring_nofiles.php', $params));
    }


    public function noDataPage() {
        $params['error'] = $this->error;

        $this->printOut(includeFile(APP_PAGE_PARTS.'monitoring_nodata.php', $params));
    }


    public function outNoData() {
        timing(); // запуск отсчёта времени процесса
        $maxProcessTime = 5; //секунд на одну итерацию поиска
        $maxObjectsPerIteration = 5; // Прерывать итерацию при большом количестве найденного
        $sut = 60*60*24; // секунд в сутках

        // Вычисляем TS конца вчерашнего дня
        $yesterday = lastMomentOfDay(time()-$sut);



        $objects = selectViewableObjectsFromSet(getAllViews());
        $ids = array_keys($objects);
        sort($ids);
        $total = count($ids);

        $noDataObjects = array();


        $J = new JSON();

        $action     = $J->readVar('action');
        $from_id    = (int)$J->readVar('from_id', 0);               // начинать поиск с указанного ид. (глобально)
        $start_id   = (int)$J->readVar('start_id', 0);              // начинать поиск с указанного ид. (текущая часть процесса)
        $from_time  = (int)$J->readVar('from_time', $yesterday);    // начинать поиск с указанного времени. (глобально)
        $start_time = (int)$J->readVar('start_time', $yesterday);   // начинать поиск с указанного времени. (текущая часть процесса)
        //$appname = mysql_escape_string($J->readVar('appname'));

        if (!Auth::isLogged()) return $J;

        //$debug_yesterday = date('Y-m-d', $yesterday);
        //$debug_start_time = date('Y-m-d', $start_time);
        //$debug_from_time = date('Y-m-d', $from_time);



        $from_time = $from_time? $from_time : $yesterday;
        //$time = ($start_time && $start_time>$yesterday );
        if ($start_time && $start_time>$yesterday ) {
            $start_time == $from_time;
            $start_id++;
        }
        $start_time = ($start_time && $start_time>=$from_time && $start_time<=$yesterday )? $start_time : $from_time;

        //$debug_start_time_after = date('Y-m-d', $start_time);
        //$debug_from_time_after = date('Y-m-d', $from_time);

        $dcount = ceil((time() - $from_time) / $sut)+1; // кол-во суток в интервале + сегодня
        $reasons = Reason::getAll($dcount); //


        $noFiles = Arr::getValue($_SESSION, 'noFiles', false);
        if (!$noFiles) $noFiles = $this->getNoFiles();

        $noLink = Arr::getValue($_SESSION, 'noLink', false);
        if (!$noLink) $noLink = $this->getNoLink();

        $exceptObj = array_merge($noFiles, $noLink);


        $last_skipped = true;
        $iterations = 0;
        $skipped = 0;
        $checked = 0;

        $time = 0;
        foreach ($ids as $id) {
            $last_skipped = false;
            if ($id < $from_id) {
                $total--;
                $last_skipped = true;
                continue;
            };

            if  ($id < $start_id) {
                $skipped++;
                $last_skipped = true;
                continue;
            }



            $time = $time? $from_time : $start_time;    // первый объект в цикле проверяем с первого дня в очереди,
                                                        // остальные - с начала интервала
            $date = date('Y-m-d', $time);


            


            $loader = $id? getDataLoader($id, $msg) : false;

            if (!$loader || !($loader instanceof Loader) || !$loader->state() || !$loader->def_floor)  {
                $noDataObjects[] = array('id'=>$id, 'date'=>$date, 'notice'=>'no_Loader');
                $checked++;
                $last_skipped = true;
                $time = $yesterday+1; // Если неисправен последний объект - нужно установить время в конец интервала для выхода из поиска
                continue;
            }
            else {
                $loader->loadFloors();
                
                // находим день последней модификации файла данных/лога
                //$obj = Arr::getValue($objects, $id, false);
                //$id_obj = Arr::getValue($obj, 'id_obj', '-1');
                $obj = $loader->asArray();
                $dfl = Arr::getValue($obj['floors']['list'], $loader->def_floor);
                
                $lastmod = 0;
                foreach ($dfl->dss as $ds) {
                    $nf_arr = Arr::getValue($exceptObj, $ds->tbl, false);
                    $lm = Arr::getValue($nf_arr, 'date', time());
                    if ($lm > $lastmod) $lastmod = $lm;
                }
                
                while ((timing()<$maxProcessTime || !$iterations) && ($time <= $yesterday) && count($noDataObjects)<$maxObjectsPerIteration) {
                    $date = date('Y-m-d', $time);
                    $date112 = date('Ymd', $time);
                    $last_skipped = false;
                    $iterations++;

                    // исключаем объекты-даты, у которых файл устарел/отсутствует
                    $nf = $time > $lastmod;
                    if (!$obj || $nf) {
                        $skipped++;
                        $last_skipped = true;
                        $time += $sut;
                        continue ; //
                    }

                    // Если отказ уже записан - пропускаем
                    $hasReason = false;
                    foreach ($reasons as $r) {
                        $to_date = Arr::getValue($r, 'to_date');
                        $trc_id = Arr::getValue($r, 'trc_id');

                        $hasReason = ($to_date == $date && $trc_id == $id);
                        if ($hasReason) break;
                    }
                    if ($hasReason) {
                        $time += $sut;
                        continue;
                    }

                    
                    $results = $loader->valueFromTable(array(
                                'what'      => "CONVERT(varchar, THEDATE, 112) as the_date",
                                'where'     => "CONVERT(varchar, THEDATE, 112) = '$date112'",
                                'groupby'   => "CONVERT(varchar, THEDATE, 112)",
                                'orderby'  => ""
                            ));

                    $sum = 0;
                    if (is_array($results)) foreach ($results as $result) {
                        while ($row = MSDB::fetchAssoc($result)){
                            $sum += Arr::getValue($row, 'sum', 0);
                            if ($sum) break;
                        }
                        if($sum) break;
                    }
                    if (!$sum) $noDataObjects[] = array('id'=>$id, 'date'=>$date, 'notice'=>'no_data');

                    $time += $sut;

                } // /while time and date
            }

            $checked++;
            if (timing()>=$maxProcessTime || count($noDataObjects)>=$maxObjectsPerIteration) {
                break;
            }



        } // /foreach ids


        if ($id == $ids[count($ids)-1] && ($time > $yesterday) || $last_skipped) {
            $J->progress = 100;
            $J->done = true;
            $_SESSION['noFiles'] = false;
            $_SESSION['noLink'] = false;
        }
        else {
            $J->progress = ceil(($skipped+$checked)*100/$total);
            $J->done = false;
        }

        $ndObjData = array();
        foreach ($noDataObjects as $obj) {
            $o = Arr::getValue($objects, $obj['id'], FALSE);
            if (is_array($o) && !array_key_exists($o['id'], $ndObjData)) $ndObjData[$o['id']] = $o;
        }

        $J->ndObjData = $ndObjData;
        $J->found = $noDataObjects;

        $J->last_time = $time;
        $J->last_date = date('Y-m-d', $time);
        $J->last_id = $id;

        return $J;

    }













    public function printOut( $content = '' ) {

        include_once APP_PAGE_PARTS.'header.php';
        echo Navbar::getHorisontalBar();
        echo Auth::loginForm(true);

        $params = array(
            'leftmenu'  => Navbar::getVerticalBar('leftmenu'),//includeFile(APP_PAGE_PARTS.'leftmenu.php'),
            'content'   => $content? $content : ''
        );
        include APP_PAGE_PARTS.'columns.php';

        include_once APP_PAGE_PARTS.'footer.php';
        die();
    }



}

Navbar::$expanded = 'lm-monitoring';
$page = new Page_mon();

if ($page->action) {
    if ($page->action == ACTIONS::MONITOR_NDLIST) echo $page->outNoData();
}
else {
    if (!Auth::isLogged()) reLocate ('index.php');

    if (array_key_exists('nodata', $_REQUEST)) {
        Navbar::$active = 'im-nodata';
        $page->noDataPage();
    }
    else if (array_key_exists('nofiles', $_REQUEST)) {
        Navbar::$active = 'im-nofiles';
        $page->noFilesPage();
    }
    else if (array_key_exists('nolink', $_REQUEST)) {
        Navbar::$active = 'im-nolink';
        $page->noLinkPage();
    }
    else  {
        reLocate ('monitoring.php?nodata');
    }
}
?>